﻿// ===============================================================================
// RouterTest.cs
// EOS Web Framework
// ===============================================================================
// Copyright (c) EOS Development Group. All rights reserved.
// ===============================================================================

namespace EOS.Framework.Web.Routing.Tests
{
    using System;
    using EOS.Framework.Contracts.Web.Routing;
    using EOS.Framework.Web.Routing.Tests.Mockups;
    using Microsoft.VisualStudio.TestTools.UnitTesting;

    /// <summary>
    /// This is the test class for the Router, which contains
    /// all Router Component tests.
    /// </summary>
    [TestClass()]
    public class RouterTest
    {
        private TestContext testContextInstance;

        /// <summary>
        /// Gets or sets the test context, which contains all information
        /// and has the functionalities.
        /// </summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        /// <summary>
        /// A test for "FindBestRoute". Router cannot work with null reference url
        /// </summary>
        [TestMethod()]
        [ExpectedException(typeof(ArgumentNullException), "Router accepts null url.")]
        public void FindBestRouteNullUrlExceptionTest()
        {
            Router router = new Router();
            router.FindBestRoute(null, "/");
        }

        /// <summary>
        /// A test for "Routes". 
        /// </summary>
        [TestMethod()]
        public void RoutesTest()
        {
            Router target = new Router();

            Route myRoute = new Route("/", typeof(Mockups.TestRouteHandler));

            target.Routes.Add("Test", myRoute);

            Assert.AreEqual(1, target.Routes.Count, "Routes contains to less or too much elements");
            Assert.AreEqual(myRoute, target.Routes["Test"], "Routes not set correctly");
        }

        /// <summary>
        /// Tests, if all routes are valid. RouteHandler type must be from a class which implements the interface.
        /// </summary>
        [TestMethod()]
        [ExpectedException(typeof(ArgumentException), "Router accepts Route with invalid handler type.")]
        public void FindBestRouteInvalidRouteHandlerTypeExceptionTest()
        {
            Router router = new Router();

            router.Routes.Add("Test", new Route("/", typeof(int)));
            router.FindBestRoute(new Uri("http://www.eos.de"), "/");
        }

        /// <summary>
        /// Tests, if the routing collection is valid.
        /// </summary>
        [TestMethod()]
        [ExpectedException(typeof(ArgumentNullException), "Route is invalid, but no Exception is thrown.")]
        public void FindBestRouteNullRouteHandlerTypeExceptionTest()
        {
            Router router = new Router();
            router.Routes.Add("Test", new Route("/", null));

            router.FindBestRoute(new Uri("http://www.eos.de"), "/");
        }

        /// <summary>
        /// A test for "FindBestRoute".
        /// </summary>
        [TestMethod]
        public void FindBestRouteRoutesTest()
        {
            Router router = new Router();

            Route validRoute = new Route("/", typeof(TestRouteHandler));

            router.Routes.Add("Test", validRoute);
            router.FindBestRoute(new Uri("http://www.eos.de"), "/");
        }

        /// <summary>
        /// A test for "FindBestRoute". Application Path must be valid.
        /// </summary>
        [TestMethod()]
        [ExpectedException(typeof(ArgumentException), "Router accepts invalid ApplicationPath.")]
        public void FindBestRouteInvalidApplicationPathExceptionTest()
        {
            Router router = new Router();
            router.FindBestRoute(new Uri("http://www.eos.de"), "AppPath");
        }

        /// <summary>
        /// A test for "FindBestRoute". Application Path cannot be empty.
        /// </summary>
        [TestMethod()]
        [ExpectedException(typeof(ArgumentException), "Router accepts empty ApplicationPath.")]
        public void FindBestRouteEmptyApplicationPathExceptionTest()
        {
            Router router = new Router();
            router.FindBestRoute(new Uri("http://www.eos.de"), string.Empty);
        }

        /// <summary>
        /// A test for "FindBestRoute". Application Path cannot be null.
        /// </summary>
        [TestMethod()]
        [ExpectedException(typeof(ArgumentNullException), "Router accepts null ApplicationPath.")]
        public void FindBestRouteNullApplicationPathExceptionTest()
        {
            Router router = new Router();
            router.FindBestRoute(new Uri("http://www.eos.de"), null);
        }

        /// <summary>
        /// Testet ob korrekte Route gefunden wurde.
        /// </summary>
        [TestMethod()]
        public void FindBestRouteWithApplicationPathTest()
        {
            Router router = new Router();

            const string url = "http://www.gpstudio.de/EOS.Cms/Part1/Part2";

            router.Routes.Add("Test", new Route("/Part1/Part2", typeof(TestRouteHandler)));
            MatchingRoute route = router.FindBestRoute(new Uri(url), "/EOS.Cms");
        }

        /// <summary>
        /// Testet ob korrekte Route gefunden wurde.
        /// </summary>
        [TestMethod()]
        public void FindBestRouteMatchingSimpleRouteTest()
        {
            Router router = new Router();

            const string domain = "http://www.gpstudio.de";

            string[] urls = new string[] 
            { 
                "/Part1", 
                "/Part1/Part",
                "/Part1/Part/Part3",
                "/Part1/Part/Part3/Part4",
            };

            int index = 0;
            foreach (string url in urls)
            {
                router.Routes.Add("Test" + index, new Route(url, typeof(TestRouteHandler)));
                index++;
            }

            foreach (string url in urls)
            {
                MatchingRoute route = router.FindBestRoute(new Uri(domain + url), "/");
                    
                Assert.AreEqual(route.Url, url, "Die falsche Url wurde gefunden.");
            }
        }

        /// <summary>
        /// Testet ob korrekte Route gefunden wurde.
        /// </summary>
        [TestMethod()]
        public void FindBestRouteMatchingParameterizedRouteTest()
        {
            Router router = new Router();

            router.Routes.Add("Test1", new Route("/Part1/Part2/", typeof(TestRouteHandler)));
            router.Routes.Add("Test2", new Route("/Part1/[parameter]/", typeof(TestRouteHandler)));

            MatchingRoute route = router.FindBestRoute(new Uri("http://www.gpstudio.de/Part1/123/"), "/");

            // Ist die gefunden Url die richtige?
            Assert.AreEqual(route.Url, "/Part1/[parameter]/", "Die falsche Url wurde gefunden.");
            // Enthält sie den Parameter "parameter"
            Assert.IsTrue(route.Parameters.ContainsKey("parameter"), "Parameter wurde nicht gesetzt");
            // Haben die Parameter die gleichen Werte.
            Assert.AreEqual((string)route.Parameters["parameter"], "123", "Parameter ist falsch");            
        }

        /// <summary>
        /// Testet ob korrekte Route gefunden wurde.
        /// </summary>
        [TestMethod()]
        public void FindBestRouteMatchingParameterizedRouteExceptionTest()
        {
            Router router = new Router();

            router.Routes.Add("Test1", new Route("/Part1/Part2/", typeof(TestRouteHandler)));
            router.Routes.Add("Test2", new Route("/Part1/[parameter]/", typeof(TestRouteHandler)));

            Assert.IsNull(router.FindBestRoute(new Uri("http://www.gpstudio.de/Part1/"), "/"));
        }

        /// <summary>
        /// Testet ob korrekte Route gefunden wurde.
        /// </summary>
        [TestMethod()]
        public void FindBestFileLinkedExceptionTest()
        {
            Router router = new Router();

            router.Routes.Add("Test1", new Route("/Part1/Part2/", typeof(TestRouteHandler)));
            router.Routes.Add("Test2", new Route("/Part1/[parameter]/", typeof(TestRouteHandler)));

            Assert.IsNull(router.FindBestRoute(new Uri("http://www.gpstudio.de/Part1.gif"), "/"));
        }

        /// <summary>
        /// Testet ob korrekte Route mit Parameter und Default werden gefunden wurde.
        /// </summary>
        [TestMethod()]
        public void FindBestRouteMatchingRouteWithDefaultsTest()
        {
            Router router = new Router();

            const string url1 = "http://www.gpstudio.de/Part1/";
            const string url2 = "http://www.gpstudio.de/Part1/123/";

            router.Routes.Add("Test1", new Route("/Part1/Part2/", typeof(TestRouteHandler)));

            Route routeWithDefaults = new Route("/Part1/[parameter]/", typeof(TestRouteHandler));
            routeWithDefaults.Defaults.Add("parameter", "456");

            router.Routes.Add("Test2", routeWithDefaults);

            MatchingRoute route = router.FindBestRoute(new Uri(url1), "/");

            // Ist die gefunden Url die richtige?
            Assert.AreEqual(route.Url, "/Part1/[parameter]/", "Die falsche Url wurde gefunden.");
            // Enthält sie den Parameter "parameter"
            Assert.IsTrue(route.Parameters.ContainsKey("parameter"), "Parameter wurde nicht gesetzt");
            // Haben die Parameter die gleichen Werte.
            Assert.AreEqual("456", (string)route.Parameters["parameter"], "Parameter ist falsch");

            route = router.FindBestRoute(new Uri(url2), "/");

            // Ist die gefunden Url die richtige?
            Assert.AreEqual(route.Url, "/Part1/[parameter]/", "Die falsche Url wurde gefunden.");
            // Enthält sie den Parameter "parameter"
            Assert.IsTrue(route.Parameters.ContainsKey("parameter"), "Parameter wurde nicht gesetzt");
            // Haben die Parameter die gleichen Werte.
            Assert.AreEqual("123", (string)route.Parameters["parameter"], "Parameter ist falsch");
        }

        /// <summary>
        /// Testet ob korrekte Route mit Parameter und Default werden gefunden wurde.
        /// </summary>
        [TestMethod()]
        public void FindBestRouteMatchingRouteWithDefaultsOnlyTest()
        {
            Router router = new Router();

            const string url = "http://www.gpstudio.de/";

            router.Routes.Add("Test1", new Route("/Part1/Part2/", typeof(TestRouteHandler)));

            Route routeWithDefaults = new Route("/", typeof(TestRouteHandler));
            routeWithDefaults.Defaults.Add("parameter", "456");

            router.Routes.Add("Test2", routeWithDefaults);

            MatchingRoute route = router.FindBestRoute(new Uri(url), "/");

            // Ist die gefunden Url die richtige?
            Assert.AreEqual(route.Url, "/", "Die falsche Url wurde gefunden.");
            // Enthält sie den Parameter "parameter"
            Assert.IsTrue(route.Parameters.ContainsKey("parameter"), "Parameter wurde nicht gesetzt");
            // Haben die Parameter die gleichen Werte.
            Assert.AreEqual("456", (string)route.Parameters["parameter"], "Parameter ist falsch");

        }

        /// <summary>
        /// Testet ob korrekte Route mit Parameter und Constraints werden gefunden wurde.
        /// </summary>
        [TestMethod()]
        public void FindBestRouteMatchingRouteWithConstraintsTest()
        {
            Router router = new Router();
            
            Route routeWithConstraints = new Route("/Part1/[parameter]/", typeof(TestRouteHandler));
            routeWithConstraints.Constraints.Add("parameter", @"\d{2}-\d{2}-\d{4}");

            router.Routes.Add("Test1", routeWithConstraints);

            MatchingRoute route = router.FindBestRoute(new Uri("http://www.gpstudio.de/Part1/12-12-1233/"), "/");

            // Ist die gefunden Url die richtige?
            Assert.AreEqual(route.Url, "/Part1/[parameter]/", "Die falsche Url wurde gefunden.");
            // Enthält sie den Parameter "parameter"
            Assert.IsTrue(route.Parameters.ContainsKey("parameter"), "Parameter wurde nicht gesetzt");
            // Haben die Parameter die gleichen Werte.
            Assert.AreEqual("12-12-1233", (string)route.Parameters["parameter"], "Parameter ist falsch");
        }

        /// <summary>
        /// Testet ob korrekte Route mit Parameter und Constraints werden gefunden wurde.
        /// </summary>
        [TestMethod()]
        public void FindBestRouteMatchingRouteWithConstraintsExceptionTest()
        {
            Router router = new Router();

            Route routeWithConstraints = new Route("/Part1/[parameter]/", typeof(TestRouteHandler));
            routeWithConstraints.Constraints.Add("parameter", @"\d{2}-\d{2}-\d{4}");

            router.Routes.Add("Test1", routeWithConstraints);

            Assert.IsNull(router.FindBestRoute(new Uri("http://www.gpstudio.de/Part1/1231/"), "/"));
        }
    }
}
